package com.hd.platform.util;

import java.math.BigDecimal;
import java.util.BitSet;
import java.util.HashMap;

public class GeohashUtil {
	
	public enum GaussSphere {
		Beijing54, Xian80, WGS84,
	}

	public static final int TEN_KILOMETERS_GEOHASH_LENGTH = 14;

	private static int MAX_LAT = 90;
	private static int MIN_LAT = -90;
	private static int MAX_LON = 180;
	private static int MIN_LON = -180;

	private static int numbits = 4 * 5;
	final static char[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
			'9', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p',
			'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };

	final static HashMap<Character, Integer> lookup = new HashMap<Character, Integer>();
	static {
		int i = 0;
		for (char c : digits)
			lookup.put(c, i++);
	}

	/**
	 * Geohash返回字符显示
	 * 
	 * @param lat
	 * @param lon
	 *            返回类型 true： base32 false： 二进制字符表示
	 * @return
	 */
	public static String encodeBin(double lat, double lon, boolean retIfBase32) {
		if ((lat < MIN_LAT) || (lat > MAX_LAT) || (lon < MIN_LON) || (lon > MAX_LON))
			return "";

		BitSet latbits = getBits(lat, MIN_LAT, MAX_LAT);
		BitSet lonbits = getBits(lon, MIN_LON, MAX_LON);
		StringBuilder buffer = new StringBuilder();
		for (int i = 0; i < numbits; i++) {
			buffer.append((lonbits.get(i)) ? '1' : '0');
			buffer.append((latbits.get(i)) ? '1' : '0');
		}
		if (retIfBase32)
			return base32(Long.parseLong(buffer.toString(), 2));
		else
			return buffer.toString();
	}

	public static String base32(long i) {
		char[] buf = new char[65];
		int charPos = 64;
		boolean negative = (i < 0);
		if (!negative)
			i = -i;
		while (i <= -32) {
			buf[charPos--] = digits[(int) (-(i % 32))];
			i /= 32;
		}
		buf[charPos] = digits[(int) (-i)];

		if (negative)
			buf[--charPos] = '-';
		return new String(buf, charPos, (65 - charPos));
	}

	private static BitSet getBits(double lat, double floor, double ceiling) {
		BitSet buffer = new BitSet(numbits);
		for (int i = 0; i < numbits; i++) {
			double mid = (floor + ceiling) / 2;
			if (lat >= mid) {
				buffer.set(i);
				floor = mid;
			} else {
				ceiling = mid;
			}
		}
		return buffer;
	}

	public static Long getStartGeoHash(Long filterGeohash, int radix) {
		return (filterGeohash >> (40 - radix)) << (40 - radix);
	}

	public static Long getEndGeoHash(Long filterGeohash, int radix) {
		return ((filterGeohash + 1) >> (40 - radix)) << (40 - radix);
	}

	public static long encodeLong(double lat, double lon) {
		String encodeBin = encodeBin(lat, lon, false);
		if (encodeBin != null && !encodeBin.equals("")) {
			return Long.valueOf(encodeBin, 2);
		} else
			return 0;
	}
	
	private static double Rad(double d) {
		return d * Math.PI / 180.0;
	}

	/**
	 * 
	 * @param lng1
	 * @param lat1
	 * @param lng2
	 * @param lat2
	 * @param gs
	 * @return 两点间的距离（单位：米）
	 */
	public static double distanceOfTwoPoints(double lng1, double lat1, double lng2, double lat2, GaussSphere gs) {
		double radLat1 = Rad(lat1);
		double radLat2 = Rad(lat2);
		double a = radLat1 - radLat2;
		double b = Rad(lng1) - Rad(lng2);
		double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
				+ Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
		s = s * (gs == GaussSphere.WGS84 ? 6378137.0 : (gs == GaussSphere.Xian80 ? 6378140.0 : 6378245.0));
		return Math.floor(s);
	}
	
	/**
	 * 
	 * @param lng1
	 * @param lat1
	 * @param lng2
	 * @param lat2
	 * @param gs
	 * @return 两点间的距离 如果距离大于1000米则返回单位为千米 反之则返回单位为米
	 */
	public static String distanceOfTwoPointsTOString(double lng1, double lat1, double lng2, double lat2, GaussSphere gs) {
		double radLat1 = Rad(lat1);
		double radLat2 = Rad(lat2);
		double a = radLat1 - radLat2;
		double b = Rad(lng1) - Rad(lng2);
		String returnLength = "";
		double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
				+ Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
		s = s * (gs == GaussSphere.WGS84 ? 6378137.0 : (gs == GaussSphere.Xian80 ? 6378140.0 : 6378245.0));
		BigDecimal bd = new BigDecimal(Math.floor(s));
		returnLength = bd.toPlainString();
		if(returnLength.length()>3){
			returnLength = returnLength.substring(0,returnLength.length()-3)+"."+returnLength.substring(returnLength.length()-3);
		}else if(returnLength.length()==3){
			returnLength = "0."+returnLength.substring(0, 2);
		}else if(returnLength.length()==2){
			returnLength = "0.0"+returnLength.substring(0, 1);
		}else{
			returnLength = "0.00";
		}
		
		return returnLength;
	}
	
}
